//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<CColorRenderTarget9.cpp>
///	@path	~/src/graphics/dx9/
///	@date	2008/09/20
///	@desc	.

#include "config/config.h"

#include "lib/system/thread_debug.h"
#include "lib/math/math_win32.h"

#include "graphics/DxHelpers.h"
#include "graphics/dx9/CColorRenderTarget9.h"

namespace xeres {

	IMPL_IOBJECT_CLASS( CColorRenderTarget9 );
	
	// CColorRenderTarget9
	CColorRenderTarget9::CColorRenderTarget9( IDirect3DTexture9 * renderTarget )
		: m_texture( renderTarget )
	{
		HRESULT hr = m_texture->GetSurfaceLevel( 0 , &m_surface );
		XS_ASSERT( SUCCEEDED(hr) );
		hr = m_texture->GetLevelDesc( 0 , &m_desc );
		XS_ASSERT( SUCCEEDED(hr) );
	}
	// ~CColorRenderTarget9
	CColorRenderTarget9::~CColorRenderTarget9( void )
	{
		if( m_surface )
		{
			m_surface->Release();
			m_surface = NULL;
		}
		if( m_texture )
		{
			UINT rest = m_texture->Release();
			if( rest != 0 )
			{
				TRACE_ERROR( _S("CColorRenderTarget9::DTOR: Invalid IDirect3DTexture9 reference count.") );
				FATAL_ABORT( _S("Invalid IDirect3DTexture9 reference count.") );
			}
			m_texture = NULL;
		}
	}
	// OnLostDevice
	bool CColorRenderTarget9::OnLostDevice( void )
	{
		// render target needs to reset
		if( m_surface )
		{
			m_surface->Release();
			m_surface = NULL;
		}
		if( m_texture )
		{
			UINT rest = m_texture->Release();
			if( rest != 0 )
			{
				TRACE_ERROR( _S("CColorRenderTarget9::OnLostDevice: Invalid IDirect3DTexture9 reference count.") );
				FATAL_ABORT( _S("Invalid IDirect3DTexture9 reference count.") );
			}
			m_texture = NULL;
		}
		return true;
	}
	// OnResetDevice
	bool CColorRenderTarget9::OnResetDevice( RefWeak<IDevice> device )
	{
		RefWeak<CDevice9> d3d9 = device;
		HRESULT hr = d3d9->GetDevice()->CreateTexture(
			m_desc.Width , m_desc.Height , 1 , m_desc.Usage | D3DUSAGE_RENDERTARGET , m_desc.Format , m_desc.Pool , &m_texture , NULL );
		if( FAILED(hr) )
		{
			TRACE_ERROR( _S("CColorRenderTarget9::OnResetDevice: Failed to re-create render target: %s") , d3d_get_err(hr) );
			return false;
		}
		hr = m_texture->GetSurfaceLevel( 0 , &m_surface );
		if( FAILED(hr) )
		{
			TRACE_ERROR( _S("CColorRenderTarget9::OnResetDevice: Failed to re-create render target: %s") , d3d_get_err(hr) );
			return false;
		}
		return true;
	}

	// GetType
	TextureType CColorRenderTarget9::GetType( void )
	{
		return TEXTURE_2D;
	}

	// GetFormat
	DXFormat CColorRenderTarget9::GetFormat( void )
	{
		return DXFormat( m_desc.Format );
	}

	// GetSize
	Pair<int> CColorRenderTarget9::GetSize( void )
	{
		return Pair<int>( m_desc.Width , m_desc.Height );
	}

	// GetDepth
	size_t CColorRenderTarget9::GetDepth( void )
	{
		FATAL_ABORT( _S("Try to get depth from 2D texture.") );
		return 0;
	}
	// IsWritable
	bool CColorRenderTarget9::IsWritable( void )
	{
		return (m_desc.Pool != D3DPOOL_DEFAULT);
	}
	// IsWriteOnly
	bool CColorRenderTarget9::IsReadable( void )
	{
		return (m_desc.Pool != D3DPOOL_DEFAULT);
	}

	// Map
	bool CColorRenderTarget9::Map( uint sub , ResourceMap& data )
	{
		if( m_texture == NULL )
			return false;

		if( sub >= m_texture->GetLevelCount() )
		{
			TRACE_ERROR( _S("CColorRenderTarget9::Map: Leve exceeded limit.") );
			return false;
		}

		if( m_desc.Pool == D3DPOOL_DEFAULT )
		{
			TRACE_ERROR( _S("CColorRenderTarget9::Map: Default pool object is not lockable.") );
			return false;
		}

		D3DLOCKED_RECT locked;
		memset( &locked , 0 , sizeof(D3DLOCKED_RECT) );
		HRESULT hr = m_texture->LockRect( sub , &locked , NULL , 0 );
		if( FAILED(hr) )
		{
			TRACE_ERROR( _S("CColorRenderTarget9::Map: Failed to lock texture: %s") ,
				d3d_get_err(hr) );
			return false;
		}
		size_t total = d3d_get_color_size( m_desc.Format ) * m_desc.Height * locked.Pitch;
		data.Map( (byte*)locked.pBits , total , locked.Pitch , m_desc.Height , m_desc.Width );
		return true;
	}

	// ReadBack
	bool CColorRenderTarget9::ReadBack( uint sub , ResourceMap& data , bool readonly )
	{
		if( m_texture == NULL )
			return false;

		if( sub >= m_texture->GetLevelCount() )
		{
			TRACE_ERROR( _S("CColorRenderTarget9::Map: Level exceeded limit.") );
			return false;
		}

		if( m_desc.Pool == D3DPOOL_DEFAULT )
		{
			TRACE_ERROR( _S("CColorRenderTarget9::Map: Default pool object is not lockable.") );
			return false;
		}

		D3DLOCKED_RECT locked;
		memset( &locked , 0 , sizeof(D3DLOCKED_RECT) );
		HRESULT hr = m_texture->LockRect( sub , &locked , NULL , readonly ? D3DLOCK_READONLY : 0 );
		if( FAILED(hr) )
		{
			TRACE_ERROR( _S("CColorRenderTarget9::Map: Failed to lock texture: %s") ,
				d3d_get_err(hr) );
			return false;
		}
		size_t total = d3d_get_color_size( m_desc.Format ) * m_desc.Height * locked.Pitch;
		data.Map( (byte*)locked.pBits , total , locked.Pitch , m_desc.Height , m_desc.Width );
		return true;
	}

	// Unmap
	bool CColorRenderTarget9::Unmap( uint sub )
	{
		if( m_texture == NULL )
			return false;
		
		HRESULT hr = m_texture->UnlockRect( sub );
		if( FAILED(hr) )
		{
			TRACE_ERROR( _S("CColorRenderTarget9::Unmap: Failed to unlock texture: %s") ,
				d3d_get_err(hr) );
			return false;
		}
		return true;
	}

	// Update
	bool CColorRenderTarget9::Update( uint sub , const Box<uint>& size , const byte * data , uint row_pitch , uint depth_pitch )
	{
		return false;
	}

	// ApplyTarget
	bool CColorRenderTarget9::ApplyTarget( RefWeak<IDevice> device )
	{
		RefWeak<CDevice9> d3d9 = device;
		HRESULT hr = d3d9->GetDevice()->SetRenderTarget( 0 , m_surface );
		if( FAILED(hr) )
		{
			TRACE_ERROR( _S("CColorRenderTarget9::Apply: Failed to set render target: %s.") , d3d_get_err(hr) );
			return false;
		}
		return true;
	}

	// GetTextureInterface
	IUnknown * CColorRenderTarget9::GetTextureInterface( void )
	{
		return m_texture;
	}

	// GetRenderTargetInterface
	IUnknown * CColorRenderTarget9::GetRenderTargetInterface( void )
	{
		return m_surface;
	}

	
} // namespace xeres
